import {request} from "@tarojs/taro";
import env from "../utils/env"
import {IOption, SuccessCallbackResult} from "./types";

export interface IAxiosOptions extends IOption<any,any>{
  needToken?:boolean // 是否传token
  params?:Record<string, any>  // get参数
  showError?:boolean // 请求出错是否提示
  simple?:boolean // 返回数据简介版(没有header等参数)
  baseUrl?:string
}
export class Axios {
  readonly baseUrl:string;
  options:IAxiosOptions;
  defaultOptions:IAxiosOptions;
  constructor(options?:IAxiosOptions) {
    options = options || {};
    const _options:IAxiosOptions = {
      showError:true,
      needToken:true,
      simple:true,
      url:"",
      header:{
        "Accept": "*/*",
        "Content-Type": 'application/json'
      },
    }
    this.baseUrl = options.baseUrl ||env.API_BASE_URL;
    this.options = Object.assign({},_options,options);
    this.defaultOptions = JSON.parse(JSON.stringify(this.options));

  }

  async _init(_url:string,options:IAxiosOptions){
    this.options = JSON.parse(JSON.stringify(this.defaultOptions));
    let url = "";
    if (/http/.test(_url)){
      url = _url
    }else {
      // 合并请求地址
      if (_url[0] === "/")url = this.baseUrl + _url;
      else url = this.baseUrl + "/" + _url;
    }
    // 组合数据
    let opt:IAxiosOptions = Object.assign({}, this.options,options);

    let {method,params} = opt
    // 当method为get时 , 将参数拼接到地址中  为post时将data转为string并放置到body中
    if (method === "GET" && typeof params==="object"){
      let str = "";
      for (let k in params){
        if (str.length === 0)str += `?`;
        str += `${k}=${params[k]}&`
      }
      str = str.substring(0,str.length-1);
      url += str;
    }
    opt.url = url;

    this.options = this._request(opt);
    return new Promise((resolve, reject) => {
      request(this.options as any).then((res)=>{
        this._response(res).then((re)=>{
          resolve(re);
        }).catch(err=>{
          reject(err);
        })
      }).catch((err)=>{
        this._response(err,false).then(res=>{
          resolve(res);
        }).catch((er)=>{
          reject(er)
        })
      })
    })
  }

  /**
   * @description 发起get 请求
   * @param url 地址
   * @param options  配置
   */
  get(url:string,options:IAxiosOptions = {}){
    options.method = "GET";
    return this._init(url,options);
  }

  /**
   * @description 发起post请求
   * @param url 地址
   * @param data 配置
   * @param options
   */
  post(url:string,data:Record<string, any>={},options:IAxiosOptions = {}){
    options.data = data;
    options.method = "POST";
    return this._init(url,options)
  }

  /**
   * @desc 发起请求 自定义
   * @param url
   * @param options
   */
  axios(url:string,options:IAxiosOptions){
    return this._init(url,options);
  }

  /**
   * @desc 请求拦截
   * @param callback (config 请求参数)  回调函数
   */
  request(callback:(config:IAxiosOptions)=>IAxiosOptions){
    this._request = (options:IAxiosOptions)=>{
      return callback(options)
    }
  }

  /**
   * @desc 内部请求处理(重写)
   * @param options
   * @private
   */
  private _request(options:IAxiosOptions):IAxiosOptions{
    return options;
  }

  /**
   * @description 响应拦截器
   * @param success 成功回调
   * @param error 失败回调
   */
  response(success:(response:SuccessCallbackResult<any>,config?:IAxiosOptions)=>Promise<any>,error?:(error:any)=>Promise<any>){
    // 重写回调拦截功能函数
    this._response = (response:SuccessCallbackResult<any>,isSuccess= true)=>{
      const conf = this.options;
      if (isSuccess){
        return success(response,conf);
      }
      else return error?error(response):Promise.reject(response);
    }
  }

  /**
   * @description 内部相应拦截处理(重写)
   * @param res 响应数据
   * @param isSuccess 是否是成功状态
   * @private
   * @return {Promise<any>} 返回一个Promise
   */
  private _response(res:SuccessCallbackResult<any>,isSuccess = true):Promise<any>{
    if (!isSuccess){
      return Promise.reject(res);
    }else return Promise.resolve(res);

  }
  /**
   * @desc 工厂模式Axios
   * @param options
   * @return {Axios}
   */
  static create(options:IAxiosOptions):Axios{
    return new Axios(options);
  }
}
